
using Poc0.Core.Objects;
using Rb.Core.Components;
using Rb.Core.Maths;
using Rb.Core.Utils;
using Rb.Interaction;
using Rb.World.Entities;

namespace Poc0.Core.Controllers
{
	/// <summary>
	/// Handles user input commands, converting them into entity actions and passing them onto the entity
	/// </summary>
	public class UserController
	{
		/// <summary>
		/// Creates this controller
		/// </summary>
		/// <param name="user">Command user that this controller listens to</param>
		/// <param name="actionTarget">Entity actions are sent to this object</param>
		public UserController( CommandUser user, IMessageHandler actionTarget )
		{
			m_Target = actionTarget;

			CommandList entityCommandList = CommandListManager.Instance.FindOrCreateFromEnum( typeof( EntityCommands ) );
			user.AddActiveListener( entityCommandList, OnCommandActive );
		}

		/// <summary>
		/// Gets/sets the target of any action commands generated by this controller
		/// </summary>
		public IMessageHandler Target
		{
			get { return m_Target; }
			set { m_Target = value; }
		}

		/// <summary>
		/// Returns the number of seconds per update tick
		/// </summary>
		private float SecondsPerUpdate
		{
			get
			{
				IMoveable moveable = Target as IMoveable;
				if ( moveable != null )
				{
					return ( float )TinyTime.ToSeconds( moveable.Travel.LastStepInterval );
				}
				return 0.1f;
			}
		}
		
		/// <summary>
		/// Sends a movement message to the target
		/// </summary>
		/// <param name="moveVec">Movement vector</param>
		private void SendMovement( Vector3 moveVec )
		{
			moveVec *= SecondsPerUpdate;
			Target.HandleMessage( new MovementXzRequest( moveVec.X, moveVec.Z ) );
		}

		/// <summary>
		/// Sends a turn message to the target
		/// </summary>
		/// <param name="turnAmount">Turn amount</param>
		private void SendTurn( float turnAmount )
		{
			turnAmount *= SecondsPerUpdate;
			Target.HandleMessage( new TurnRequest( turnAmount ) );
		}

		/// <summary>
		/// Gets the targets run speed
		/// </summary>
		private float TargetRunSpeed
		{
			get
			{
				IBody body = Parent.GetType< IBody >( Target );
				return ( body == null ) ? 1.0f : body.RunSpeed;
			}
		}

		/// <summary>
		/// Gets the targets run speed
		/// </summary>
		private float TargetWalkSpeed
		{
			get
			{
				IBody body = Parent.GetType< IBody >( Target );
				return ( body == null ) ? 1.0f : body.WalkSpeed;
			}
		}
		
		/// <summary>
		/// Gets the targets run speed
		/// </summary>
		private float TargetTurnSpeed
		{
			get
			{
				IBody body = Parent.GetType< IBody >( Target );
				float degrees = ( body == null ) ? 1.0f : body.TurnSpeed;

				return Constants.DegreesToRadians * degrees;
			}
		}

		/// <summary>
		/// Returns the targets X axis
		/// </summary>
		private Vector3 TargetXAxis
		{
			get
			{
				IPlaceable placeable = Parent.GetType< IPlaceable >( Target );
				return placeable == null ? Vector3.XAxis : placeable.Frame.XAxis;
			}
		}

		/// <summary>
		/// Returns the targets Z axis
		/// </summary>
		private Vector3 TargetZAxis
		{
			get
			{
				IPlaceable placeable = Parent.GetType< IPlaceable >( Target );
				return placeable == null ? Vector3.ZAxis : placeable.Frame.ZAxis;
			}
		}

		/// <summary>
		/// Called when a command in the entity command list is activated
		/// </summary>
		/// <param name="msg"></param>
		private void OnCommandActive( CommandMessage msg )
		{
			if ( Target == null )
			{
				return;
			}

			switch ( ( EntityCommands )msg.CommandId )
			{
				case EntityCommands.MoveForward :
				{
					SendMovement( TargetZAxis * TargetRunSpeed );
					break;
				}
				case EntityCommands.MoveBackward :
				{
					SendMovement( TargetZAxis * -TargetRunSpeed );
					break;
				}
				case EntityCommands.StrafeLeft :
				{
					SendMovement( TargetXAxis * -TargetRunSpeed );
					break;
				}
				case EntityCommands.StrafeRight :
				{
					SendMovement( TargetXAxis * TargetRunSpeed );
					break;
				}
				case EntityCommands.TurnLeft :
				{
					SendTurn( -TargetTurnSpeed );
					break;
				}
				case EntityCommands.TurnRight :
				{
					SendTurn( TargetTurnSpeed );
					break;
				}
				case EntityCommands.Jump :
				{
					break;
				}
				case EntityCommands.Fire :
				{
					Target.HandleMessage( new FireWeaponMessage( ) );
					break;
				}
			}
		}

		private IMessageHandler m_Target;
	}
}
